﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Linq;
using System.Xml.Linq;
using System.Text.RegularExpressions;
//
using PerceptiveMCAPI.Types.Internal;
//
namespace PerceptiveMCAPI.Types
{
	public class listMemberInfoOutput : Api_BaseOutput
	{
		public listMemberInfoResults result { get; set; }
		public listMemberInfoParms inputParms { get; private set; }
		// --------------------------------------------------------
		public listMemberInfoOutput( listMemberInfoInput input )
			: base( input )
		{
			inputParms = input.parms;
			result = new listMemberInfoResults();
		}
		// --------------------------------------------------- tostring
		public override string ToString()
		{
			return string.Format( "listMemberInfo - ToString TBD" );
		}

		#region ================================================================== format setup
		// ---- serial
		internal void Format( string api_Request, string api_Response )
		{
			this.api_Request = api_Request;
			this.api_Response = api_Response;
			// ----------------------------------
			if ( this.api_OutputType == EnumValues.OutputType.JSON )
				outputJSON();
			else if ( this.api_OutputType == EnumValues.OutputType.XML )
				outputXML();
		}
		// --------------------------------------------------------------------
		// ---- xmlrpc
		internal void Format( x_listMemberInfo rpcResult )
		{
			this.api_Request = EnumValues.NotSupplied;
			this.api_Response = EnumValues.NotSupplied;
			// ------------------------
			this.result = FormatResults( rpcResult );
		}
		#endregion

		#region ================================================================= Output
		// ---------------------------------------------------------- outputXML
		internal void outputXML()
		{
			XElement xml = XElement.Parse( this.api_Response );
			// error check
			Api_Error error = apiSerial.GetXmlOutputError( xml );
			if ( error != null )
			{
				this.api_ErrorMessages.Add( error );
				return;
			}
			// -------------------------------------------- get values
			try
			{
				listMemberInfoResults results = new listMemberInfoResults();
				DateTime dt; int it; double dbl;
				int.TryParse( xml.Element( "success" ).Value, out it );
				results.success = it;
				int.TryParse( xml.Element( "errors" ).Value, out it );
				results.errors = it;
				IEnumerable<XElement> mbrlist = xml.Element( "data" ).Elements( "struct" );
				foreach ( XElement entry in mbrlist )
				{
					//
					if ( entry.Element( "error" ) != null )
					{
						listMemberInfoResults.ErrorInfo errinfo = new listMemberInfoResults.ErrorInfo();
						errinfo.email = entry.Element( "email_address" ).Value;
						errinfo.error = entry.Element( "error" ).Value;
						results.errordata.Add( errinfo );
					}
					else
					{
						listMemberInfoResults.MemberInfo mbrinfo = new listMemberInfoResults.MemberInfo();
						//
						mbrinfo.email = entry.Element( "email" ).Value;
						mbrinfo.id = entry.Element( "id" ).Value;
						mbrinfo.email_type = apiHelper.GetEnumFromString<EnumValues.emailType>( entry.Element( "email_type" ).Value );
						mbrinfo.status = apiHelper.GetEnumFromString<EnumValues.listMembers_status>( entry.Element( "status" ).Value );
						mbrinfo.ip_opt = entry.Element( "ip_opt" ).Value;
						mbrinfo.ip_signup = entry.Element( "ip_signup" ).Value;
						if ( entry.Element( "member_rating" ) != null )
						{
							int.TryParse( entry.Element( "member_rating" ).Value, out it );
							mbrinfo.member_rating = it;
						}
						if ( entry.Element( "campaign_id" ) != null )
							mbrinfo.campaign_id = entry.Element( "campaign_id" ).Value;
						// -------------------------------------------------------------- lists
						if ( entry.Element( "lists" ).HasElements )
						{
							IEnumerable<XElement> xlists = entry.Element( "lists" ).Elements();
							foreach ( XElement xl in xlists )
							{
								EnumValues.listMembers_status xlsts = EnumValues.listMembers_status.NotSpecified;
								string xlname = "";
								// -- happens if numeric listid -- bad xml parser
								if ( xl.Name == "struct" )
									xlname = xl.Attribute( "key" ).Value ?? "* missing *";
								else
									xlname = xl.Name.LocalName;
								if ( !string.IsNullOrEmpty( xl.Value ) )
									xlsts = apiHelper.GetEnumFromString<EnumValues.listMembers_status>( xl.Value );
								mbrinfo.lists.Add( xlname, xlsts );
							}
						}
						DateTime.TryParse( entry.Element( "timestamp" ).Value, out dt );
						mbrinfo.timestamp = dt;
						DateTime.TryParse( entry.Element( "info_changed" ).Value, out dt );
						mbrinfo.info_changed = dt;
						int.TryParse( entry.Element( "web_id" ).Value, out it );
						mbrinfo.web_id = it;
						// -------------------------------------------------------------- merges
						if ( entry.Element( "merges" ).HasElements )
						{
							IEnumerable<XElement> merge_vars = entry.Element( "merges" ).Elements();
							foreach ( XElement mv in merge_vars )
							{
								if ( mv.Name.LocalName.ToLower() == "groupings" )
								{
									List<interestGroupings> groupings = new List<interestGroupings>();
									IEnumerable<XElement> grouping_elem = mv.Elements( "struct" );
									foreach ( XElement elem in grouping_elem )
									{
										interestGroupings grouping = new interestGroupings();
										int.TryParse( elem.Element( "id" ).Value, out it );
										grouping.id = it;
										grouping.name = elem.Element( "name" ).Value;
										//
										// -- this didn't work when commas are in the groups; codeplex issue #5381
										// grouping.groups = apiHelper.MakeListFromString( elem.Element( "groups" ).Value );
										// -- thanks go out to 'checksix' for the below regex solution
										//
										grouping.groups = Regex.Split( elem.Element( "groups" ).Value, @"(?<=[^\\])," ).Select( R => R.Replace( @"\,", "," ).Trim() ).ToList();

										groupings.Add( grouping );
									}
									mbrinfo.groupings = groupings;
								}
								else if ( mv.HasElements )
								{
									IEnumerable<XElement> mv_array = mv.Elements();
									Dictionary<string, object> dict = new Dictionary<string, object>();
									foreach ( XElement mva in mv_array )
									{
										dict.Add( mva.Name.LocalName, mva.Value );
									}
									mbrinfo.merges.Add( mv.Name.LocalName, dict );
								}
								else
									mbrinfo.merges.Add( mv.Name.LocalName, mv.Value );
							}
						}
						// -------------------------------------------------------------- geo
						if ( entry.Element( "geo" ).HasElements )
						{
							XElement xg = entry.Element( "geo" );
							var geo = new listMemberInfoResults.MemberInfo.geo_data();
							//
							double.TryParse( xg.Element( "latitude" ).Value, out dbl );
							geo.latitude = dbl;
							double.TryParse( xg.Element( "longitude" ).Value, out dbl );
							geo.longitude = dbl;
							int.TryParse( xg.Element( "gmtoff" ).Value, out it );
							geo.gmtoff = it;
							int.TryParse( xg.Element( "dstoff" ).Value, out it );
							geo.dstoff = it;
							geo.timezone = xg.Element( "timezone" ).Value;
							geo.countrycode = xg.Element( "cc" ).Value;
							geo.region = xg.Element( "region" ).Value;
							//
							mbrinfo.geo = geo;
						}
						// -------------------------------------------------------------- clients
						if ( entry.Element( "clients" ).HasElements )
						{
							// TODO: find a structure example
						}
						// -------------------------------------------------------------- static_segments
						if ( entry.Element( "static_segments" ).HasElements )
						{
							IEnumerable<XElement> segments = entry.Element( "static_segments" ).Elements( "struct" );
							foreach ( XElement seg in segments )
							{
								DateTime.TryParse( seg.Element( "added" ).Value, out dt );
								int.TryParse( seg.Element( "id" ).Value, out it );
								mbrinfo.static_segments.Add(
								new listMemberInfoResults.MemberInfo.segment
								{
									id = it,
									name = seg.Element( "name" ).Value,
									added = dt
								} );
							}
						}
						// ------------------------------
						results.data.Add( mbrinfo );
					}
				}
				this.result = results;
			}
			catch ( Exception ex )
			{
				this.api_ErrorMessages.Add( apiException.FormatError( ex ) );
			}
		}

		// ---------------------------------------------------------- outputJSON
		internal void outputJSON()
		{
			JavaScriptSerializer jss = new JavaScriptSerializer();

			try
			{
				if ( this.api_Response.Contains( EnumValues.JSONErrorString ) )
				{
					Api_Error err = jss.Deserialize<Api_Error>( this.api_Response );
					this.api_ErrorMessages.Add( err );
				}
				else
				{
					x_listMemberInfo jsonResult = jss.Deserialize<x_listMemberInfo>( this.api_Response );
					this.result = FormatResults( jsonResult );
				}
			}
			catch ( Exception ex )
			{
				this.api_ErrorMessages.Add( apiException.FormatError( ex ) );
			}
		}

		// ---------------------------------------------------------- FormatResults
		private listMemberInfoResults FormatResults( x_listMemberInfo x_results )
		{
			DateTime dt;
			listMemberInfoResults results = new listMemberInfoResults();
			results.success = x_results.success;
			results.errors = x_results.errors;
			//
			foreach ( var entry in x_results.data )
			{
				if ( entry.error != null || !string.IsNullOrEmpty( entry.error ) )
				{
					// --------------------------------------------------- ErrorInfo
					var err = new listMemberInfoResults.ErrorInfo();
					err.email = entry.email_address;
					err.error = entry.error;
					results.errordata.Add( err );
				}
				else
				{
					// --------------------------------------------------- MemberInfo
					var info = new listMemberInfoResults.MemberInfo();
					info.id = entry.id;
					info.email = entry.email;
					info.email_type = apiHelper.GetEnumFromString<EnumValues.emailType>( entry.email_type );
					info.status = apiHelper.GetEnumFromString<EnumValues.listMembers_status>( entry.status );
					info.ip_opt = entry.ip_opt;
					info.ip_signup = entry.ip_signup;
					info.member_rating = entry.member_rating;
					if ( entry.campaign_id != null ) info.campaign_id = entry.campaign_id;
					DateTime.TryParse( entry.timestamp, out dt );
					info.timestamp = dt;
					DateTime.TryParse( entry.info_changed, out dt );
					info.info_changed = dt;
					info.web_id = entry.web_id;
					// -------------------------------------------------------- do merge & list converts
					Dictionary<string, object> merge_vars;
					List<interestGroupings> groupings;
					FormatMerges( entry.merges, out merge_vars, out groupings );      // mergevars and groupings split out
					info.merges = merge_vars;
					info.groupings = groupings;
					info.lists = FormatLists( entry.lists );
					// -------------------------------------------------------- do the new v1.3 stuff here
					// geo
					info.geo = FormatGeo( entry.geo );
					// clients
					info.clients = FormatClients( entry.clients );
					// static segments
					info.static_segments = FormatSegments( entry.static_segments );
					// --------------
					results.data.Add( info );
				}
			}
			//------------
			return results;
		}

		//
		private void FormatMerges( object mergeObject,
			out Dictionary<string, object> merge_vars,
			out List<interestGroupings> groupings )
		{
			merge_vars = new Dictionary<string, object>();
			groupings = new List<interestGroupings>();
			int it;
			// object is array that is empty if no merge variables
			if ( mergeObject == null || mergeObject.GetType().IsArray ) return;

			IDictionary merges = mergeObject as IDictionary;
			IDictionaryEnumerator de = merges.GetEnumerator();
			while ( de.MoveNext() )
			{
				if ( de.Key.ToString().ToLower() == "groupings" )
				{
					object[] groupings_obj = de.Value as object[];
					foreach ( object grouping_obj in groupings_obj )
					{
						interestGroupings grouping = new interestGroupings();
						IDictionary grouping_dict = grouping_obj as IDictionary;
						int.TryParse( grouping_dict["id"].ToString(), out it );
						grouping.id = it;
						grouping.name = grouping_dict["name"].ToString();
						string groups_string = grouping_dict["groups"].ToString();
						//
						// -- this didn't work when commas are in the groups; codeplex issue #5381
						// grouping.groups = apiHelper.MakeListFromString( groups_string ); 
						// -- thanks go out to 'checksix' for the below regex solution
						//
						grouping.groups = Regex.Split( groups_string, @"(?<=[^\\])," ).Select( R => R.Replace( @"\,", "," ).Trim() ).ToList();
						//
						groupings.Add( grouping );
					}
				}
				else if ( de.Value.GetType().Name.ToLower().Contains( "dictionary" )
					 || de.Value.GetType().Name.ToLower().Contains( "xmlrpcstruct" ) )
				{
					IDictionary varDict = de.Value as IDictionary;
					IDictionaryEnumerator vd = varDict.GetEnumerator();
					Dictionary<string, object> newDict = new Dictionary<string, object>();
					while ( vd.MoveNext() )
					{
						newDict.Add( vd.Key.ToString(), vd.Value.ToString() );
					}
					merge_vars.Add( de.Key.ToString(), newDict );
				}
				else
					merge_vars.Add( de.Key.ToString(), de.Value );
			}
		}
		// -----------------------
		private Dictionary<string, EnumValues.listMembers_status> FormatLists( object listObject )
		{
			Dictionary<string, EnumValues.listMembers_status> lists = new Dictionary<string, EnumValues.listMembers_status>();

			// object is array that is empty if no merge variables
			if ( listObject == null || listObject.GetType().IsArray ) return lists;
			//
			IDictionary list = listObject as IDictionary;
			IDictionaryEnumerator le = list.GetEnumerator();

			while ( le.MoveNext() )
			{
				// MC passes back bad data (null) in xml-rpc when list id is numeric -- check for it
				// hopefully they will be able fix this problem
				string lid = "";
				if ( le.Key == null || string.IsNullOrEmpty( le.Key.ToString() ) )
					lid = "* missing *";
				else lid = le.Key.ToString();

				lists.Add( lid,
					apiHelper.GetEnumFromString<EnumValues.listMembers_status>( le.Value.ToString() ) );
			}
			//
			return lists;
		}
		// ------------------------------------------------------------- FormatSegments
		private List<listMemberInfoResults.MemberInfo.segment> FormatSegments( x_listMemberInfo.data_item.static_seg[] x_static_seg )
		{
			DateTime dt;
			var segments = new List<listMemberInfoResults.MemberInfo.segment>();
			//
			foreach ( var seg in x_static_seg )
			{
				DateTime.TryParse( seg.added, out dt );
				segments.Add( new listMemberInfoResults.MemberInfo.segment
				{
					id = seg.id,
					name = seg.name,
					added = dt
				} );
			}
			//
			return segments;
		}
		// ------------------------------------------------------------- FormatGeo
		private listMemberInfoResults.MemberInfo.geo_data FormatGeo( object x_geo )
		{
			double dbl; int it;
			var geo = new listMemberInfoResults.MemberInfo.geo_data();
			IDictionary xg = x_geo as IDictionary;
			if ( xg == null ) return geo;
			//
			double.TryParse( xg["latitude"].ToString(), out dbl );
			geo.latitude = dbl;
			double.TryParse( xg["longitude"].ToString(), out dbl );
			geo.longitude = dbl;
			int.TryParse( xg["gmtoff"].ToString(), out it );
			geo.gmtoff = it;
			int.TryParse( xg["dstoff"].ToString(), out it );
			geo.dstoff = it;
			geo.timezone = xg["timezone"].ToString();
			geo.countrycode = xg["cc"].ToString();
			geo.region = xg["region"].ToString();
			//
			return geo;
		}
		// ------------------------------------------------------------- FormatClients
		private List<listMemberInfoResults.MemberInfo.client_data> FormatClients( object x_clients )
		{
			var clients = new List<listMemberInfoResults.MemberInfo.client_data>();
			//
			//
			return clients;
		}


		#endregion
	}
}
